package com.hrcx.mystery.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hrcx.mystery.common.CommonPage;
import com.hrcx.mystery.common.PageParamRequest;
import com.hrcx.mystery.constants.Constants;
import com.hrcx.mystery.dto.*;
import com.hrcx.mystery.dto.admin.NewMysteryDto;
import com.hrcx.mystery.dto.admin.UpdateMysteryDto;
import com.hrcx.mystery.exception.MysteryException;
import com.hrcx.mystery.pojo.*;
import com.hrcx.mystery.utils.quartz.cron.CronParser;
import com.hrcx.mystery.vo.*;
import com.hrcx.mystery.service.*;
import com.hrcx.mystery.mapper.EbMysteryMapper;
import com.hrcx.mystery.utils.AliasMethod;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author LinXiPeng
 */
@Service
@Slf4j
@Transactional(isolation = Isolation.READ_UNCOMMITTED, rollbackFor = Throwable.class)
public class EbMysteryServiceImpl extends ServiceImpl<EbMysteryMapper, EbMystery>
        implements EbMysteryService {
    /**
     * 模拟用户编号
     */
    private final int uid = 1;
    /**
     * 设置bean的名称 一定要为IOC容器中的实例
     */
    private final String simpleName = this.getClass().getSimpleName().substring(0, 1).toLowerCase() + this.getClass().getSimpleName().substring(1);
    /**
     * 按照每秒进行定时任务
     */
    private final String tempCron = "* * * * * ?";

    /**
     * 注入盲盒数据交互接口
     */
    @Resource
    private EbMysteryMapper ebMysteryMapper;

    /**
     * 注入商家数据交互接口
     */
    @Resource
    private EbMerchantService ebMerchantService;

    /**
     * 注入盲盒商品扩展服务
     */
    @Resource
    private EbMysteryMerchandiseAssociatedService associatedService;

    /**
     * 注入原始商品服务
     */
    @Resource
    private EbStoreProductService ebStoreProductService;

    /**
     * 注入盲盒卡卷服务
     */
    @Resource
    private EbMysteryCardVolumeService cardVolumeService;

    /**
     * 注入盒柜服务
     */
    @Resource
    private EbMysteryUserBoxArkService userBoxArkService;

    /**
     * 注入开箱记录服务
     */
    @Resource
    private EbMysteryOutOfRecordService recordService;

    /**
     * 注入盲盒收藏服务
     */
    @Resource
    private EbMysteryUserCollectService collectService;

    /**
     * 注入用户拓展类型
     */
    @Resource
    private EbMysteryUserExtensionService userExtensionService;

    /**
     * 注入用户卡卷记录
     */
    @Resource
    private EbMysteryUserCardVolumeService userCardVolumeService;

    /**
     * 注入奖品回收记录服务
     */
    @Resource
    private EbMysteryRecycleRecordService recycleRecordService;

    /**
     * 注入定时任务服务
     */
    @Resource
    private EbMysteryScheduleJobService scheduleJobService;

    /**
     * 注入奖池累计记录服务
     */
    @Resource
    private EbMysteryPondRecordService pondRecordService;

    /**
     * 注入盲盒配置服务
     */
    @Resource
    private EbMysteryConfigService configService;

    @Override
    public PageInfo<EbMysteryVo> getMysteryList(MysterySearchDto searchRequest, PageParamRequest pageParamRequest) {
        //根据分页请求参数实例pageHelper
        Page<EbMystery> ebMysteriesPage = PageHelper.startPage(pageParamRequest.getPage(), pageParamRequest.getLimit());
        //创建多条件包装类 由于需要多字段动态排序所以选择QueryWrapper
        QueryWrapper<EbMystery> mysteryQueryWrapper = Wrappers.query();
        //如果有编号那么其他条件忽略
        if (searchRequest.getBid() != null && searchRequest.getBid() != 0) {
            mysteryQueryWrapper.eq("id", searchRequest.getBid());
        } else {
            //其他条件
            //判断分区
            if (searchRequest.getScope() != null) {
                mysteryQueryWrapper.eq("scope", searchRequest.getScope());
            }
            //判断关键字
            if (StringUtils.isNotBlank(searchRequest.getKeywords())) {
                //可多个字段模糊查找
                mysteryQueryWrapper.and(a -> a.or().like("name", searchRequest.getKeywords()));
            }
            //盲盒状态
            if (searchRequest.getStatus() != null) {
                mysteryQueryWrapper.eq("status", searchRequest.getStatus());
            }
            //是否为用户查询自己的收藏盒子
            if (searchRequest.getUserCollect() != null && searchRequest.getUserCollect()) {
                //先看看当前用户是否登录,如果没有登录将抛出异常，告知用户请先登录
                //throw new MysteryException("抱歉，请先登录!");
                //查出用户收藏盲盒列表 只需要盒子编号
                List<EbMysteryUserCollect> userCollects = collectService.lambdaQuery()
                        .eq(EbMysteryUserCollect::getUid, uid).select(EbMysteryUserCollect::getBid).list();
                //获得盒子编号集合
                for (EbMysteryUserCollect userCollect : userCollects) {
                    mysteryQueryWrapper.or().eq("id", userCollect.getBid());
                }
            }
            //如果没有指定排序方式,那么默认就降序排序
            if (StringUtils.isBlank(searchRequest.getOrderType()) && !StringUtils.isBlank(searchRequest.getOrderColumns())) {
                //根据条件排序,排序一定要放在最后 如果传入的为空或为null就默认都为降序
                if (searchRequest.getOrderType().equals(Constants.SORT_ASC)) {
                    //升序
                    mysteryQueryWrapper.orderByAsc(searchRequest.getOrderColumns());
                } else {
                    //降序 根据前端传入的字段排序
                    mysteryQueryWrapper.orderByDesc(searchRequest.getOrderColumns());
                }
            }
        }
        //根据条件包装器进行查询
        List<EbMystery> ebMysteries = ebMysteryMapper.selectList(mysteryQueryWrapper);
        //盲盒查完了查每个盲盒对应的商家
        //创建盲盒响应集合
        List<EbMysteryVo> ebMysteryRes = new ArrayList<>();
        //遍历已经查询完的盲盒集合
        for (EbMystery ebMystery : ebMysteries) {
            //创建盲盒响应实例
            EbMysteryVo ebMysteryVo = new EbMysteryVo();
            //将盲盒原始对象字段中查到的数据复制给盲盒响应实例
            BeanUtils.copyProperties(ebMystery, ebMysteryVo);
            //调用商家服务，找到该id的商家对象
            ebMysteryVo.setCreatby(ebMerchantService.getById(ebMystery.getCreatby()));
            //将填充完的响应对象放到数组中
            ebMysteryRes.add(ebMysteryVo);
        }
        return CommonPage.copyPageInfo(ebMysteriesPage, ebMysteryRes);
    }

    @Override
    public List<EbMysteryMerchandiseAssociatedVo> getMysteryProdList(Integer bid) {
        //根据盲盒编号找到关联的商品扩展信息
        List<EbMysteryMerchandiseAssociated> associatedList = associatedService.lambdaQuery()
                .eq(EbMysteryMerchandiseAssociated::getBid, bid).list();
        //创建盲盒商品响应对象的集合
        ArrayList<EbMysteryMerchandiseAssociatedVo> associatedResponses = new ArrayList<>();
        //遍历得到的扩展信息中的商品编号，找到商品表中的详细信息
        for (EbMysteryMerchandiseAssociated associated : associatedList) {
            //添加至商品响应集合
            associatedResponses.add(transMerchandiseAssociated(associated));
        }
        //将填充好的响应集合返回
        return associatedResponses;
    }

    @Override
    public List<EbMysteryMerchandiseAssociatedVo> openMystery(MysteryOpenDto openRequest) {
        //获得盲盒状态是否开启
        EbMystery ebMystery = this.lambdaQuery().eq(EbMystery::getId, openRequest.getBid()).one();
        //检查当前用户是否有该盒子开箱机会 (这里先测试用户id ：1 后面直接使用security中的当前对象即可)
        if (ebMystery.getStatus()) {
            EbMysteryUserBoxArk userBoxArk = userBoxArkService.lambdaQuery()
                    //根据关联的盒子编号和用户编号找到用户对应的盒子信息
                    .eq(EbMysteryUserBoxArk::getBid, openRequest.getBid()).eq(EbMysteryUserBoxArk::getUid, uid)
                    //我只需要数量,其他不要
                    .select(EbMysteryUserBoxArk::getCount, EbMysteryUserBoxArk::getId)
                    .one();
            //判断用户剩余抽奖次数是否大于或等于连抽次数，如果响应的数据为空，说明用户根本就没有购买过该盒子的开盒机会
            if (userBoxArk != null && userBoxArk.getCount() >= openRequest.getContinuousNum()) {
                //根据盲盒编号找到关联的商品扩展信息
                List<EbMysteryMerchandiseAssociated> associatedList = associatedService.lambdaQuery()
                        .eq(EbMysteryMerchandiseAssociated::getBid, ebMystery.getId()).list();
                List<EbMysteryMerchandiseAssociatedLotteryVo> associatedLotteryVos = new ArrayList<>();
                //遍历盲盒扩展信息，找到每个奖品的详细信息
                for (EbMysteryMerchandiseAssociated associated : associatedList) {
                    EbMysteryMerchandiseAssociatedLotteryVo ebMysteryMerchandiseAssociatedLotteryVo = new EbMysteryMerchandiseAssociatedLotteryVo();
                    BeanUtils.copyProperties(associated, ebMysteryMerchandiseAssociatedLotteryVo);
                    //获得商品详细
                    if (associated.getType()) {
                        //调用商品服务，得到商品的详细信息
                        ebMysteryMerchandiseAssociatedLotteryVo.setProduct(ebStoreProductService.lambdaQuery()
                                .eq(EbStoreProduct::getId, associated.getPid())
                                //为了较少查询耗时，选择需要的字段即可
                                .select(EbStoreProduct::getId, EbStoreProduct::getCost, EbStoreProduct::getPrice, EbStoreProduct::getStoreName)
                                .one());
                    } else {
                        //调用卡卷服务，获得卡卷的信息
                        ebMysteryMerchandiseAssociatedLotteryVo.setCardVolume(cardVolumeService.getById(associated.getPid()));
                    }
                    associatedLotteryVos.add(ebMysteryMerchandiseAssociatedLotteryVo);
                }
                //通过集合流，筛选出符合条件的奖品名单，并通过概率排序
                Stream<EbMysteryMerchandiseAssociatedLotteryVo> lotteryVoStream = associatedLotteryVos.stream();
                //查询盒子内所有奖品的详细信息，一次查出，避免与数据库建立的连接过多导致系统崩溃
                //设置参与开奖的奖品名单
                List<EbMysteryMerchandiseAssociatedLotteryVo> drawingList = lotteryVoStream.filter(a -> {
                    //如果是虚拟奖品就直接放入奖品名单
                    //如果是计算成本 那么就看看价格是否小于利润比
                    if (a.getProjectCost()) {
                        //利润比的金额
                        BigDecimal pondProfit = ebMystery.getPond().multiply(BigDecimal.valueOf(ebMystery.getProfitShare()));
                        //如果利润比后的金额为0，则参与开奖
                        if (pondProfit.compareTo(BigDecimal.valueOf(0)) == 0) {
                            return true;
                        }
                        //判断奖品类型（实物/虚物）
                        if (a.getType()) {
                            //如果当前奖品的成本价低于所设置的利润比金额，则参与开奖
                            return a.getProduct().getCost().compareTo(pondProfit) < 1;
                        }
                        //虚拟奖品 计成本
                        return a.getCardVolume().getCost().compareTo(pondProfit) < 1;
                    } else {
                        //盲盒上限阈值
                        BigDecimal upperThreshold = ebMystery.getUpperThreshold();
                        //如果上限为0，则表示没有上限
                        if (upperThreshold.compareTo(BigDecimal.valueOf(0)) == 0) {
                            return true;
                        }
                        if (a.getType()) {
                            //不计成本的奖品看看是否低于设置的上限阈值
                            return a.getProduct().getPrice().compareTo(upperThreshold) < 1;
                        }
                        return a.getCardVolume().getPrice().compareTo(upperThreshold) < 1;
                    }
                }).sorted(Comparator.comparing(EbMysteryMerchandiseAssociatedLotteryVo::getRealProbability)).collect(Collectors.toList());
                //生成概率数组
                List<Double> probabilityList = drawingList.stream().map(EbMysteryMerchandiseAssociatedLotteryVo::getRealProbability).collect(Collectors.toList());
                //创建一个奖品集合，用于装载返回并记录
                List<EbMysteryMerchandiseAssociatedVo> associatedResponses = new ArrayList<>();
                for (int i = 0; i < openRequest.getContinuousNum(); i++) {
                    //将概率数组交给别名算法，算出抽到商品索引
                    int index = new AliasMethod(probabilityList).next();
                    //当前被抽中的商品（扩展类）
                    EbMysteryMerchandiseAssociatedLotteryVo associated = drawingList.get(index);
                    //更新盲盒奖池金额，开箱次数+1
                    //计算利润，获得盲盒开盒单价-奖品成本
                    BigDecimal cost = associated.getType() ? associated.getProduct().getCost() : associated.getCardVolume().getCost();
                    BigDecimal profit = ebMystery.getPrice()
                            .subtract(cost);
                    this.lambdaUpdate().eq(EbMystery::getId, ebMystery.getId())
                            .setSql(String.format("opennum = opennum+1,pond=pond+%s,cost_pond=cost_pond+%s", profit, cost)).update();
                    //增加当前商品被抽中的次数
                    associatedService.lambdaUpdate().eq(EbMysteryMerchandiseAssociated::getId, associated.getId())
                            .setSql("hits=hits+1").update();
                    //将开箱记录添加至数据库
                    //保存前先重置自增值
                    recordService.resetIncrement();
                    recordService.save(new EbMysteryOutOfRecord(uid, associated.getId(), new Date(), 0));
                    //用户开箱机会-1
                    userBoxArkService.lambdaUpdate().eq(EbMysteryUserBoxArk::getId, userBoxArk.getId()).setSql("count = count-1").update();
                    //系统设置的积分加成峰值
                    EbMysteryConfig topScore = configService.getById(1);
                    int i1 = Integer.parseInt(topScore.getVal());
                    //系统设置的积分加成值额
                    EbMysteryConfig addScore = configService.getById(2);
                    int i2 = Integer.parseInt(addScore.getVal());
                    EbMysteryUserExtension one = userExtensionService.lambdaQuery().eq(EbMysteryUserExtension::getUid, uid)
                            .select(EbMysteryUserExtension::getScore).one();
                    //如果用户积分+10小于系统配置的峰值
                    if (one.getScore() + i2 < i1) {
                        //用户的积分+10
                        userExtensionService.lambdaUpdate().eq(EbMysteryUserExtension::getUid, uid)
                                .setSql(String.format("score = score+%s", i2)).update();
                    }
                    EbMysteryMerchandiseAssociatedVo ebMysteryMerchandiseAssociatedVo = new EbMysteryMerchandiseAssociatedVo();
                    BeanUtils.copyProperties(associated, ebMysteryMerchandiseAssociatedVo);
                    log.info(String.format("用户抽中了%s，盲盒单价为%s,奖品成本为%s，利润为%s",
                            associated.getType() ? associated.getProduct().getStoreName() : associated.getCardVolume().getName(),
                            ebMystery.getPrice(),
                            associated.getType() ? associated.getProduct().getCost() : associated.getCardVolume().getCost(), profit));
                    associatedResponses.add(ebMysteryMerchandiseAssociatedVo);
                }
                //将抽中的商品响应给控制器
                return associatedResponses;
            }
            throw new MysteryException(String.format("抽奖次数大于拥有盒子数量,还需购买%s盒子!", openRequest.getContinuousNum() - (userBoxArk != null ? userBoxArk.getCount() : 0)));
        }
        throw new MysteryException("该盒子暂未开启,先试试其他的盒子吧!");
    }

    @Override
    public Boolean collect(Integer bid) {
        //看看当前用户的是否已经收藏该盲盒,如果收藏存在,则取消收藏
        EbMysteryUserCollect collect = collectService.lambdaQuery()
                .eq(EbMysteryUserCollect::getUid, uid)
                .eq(EbMysteryUserCollect::getBid, bid)
                .one();
        if (collect != null && collect.getId() != null) {
            //取消收藏
            return collectService.lambdaUpdate()
                    .eq(EbMysteryUserCollect::getBid, bid)
                    .eq(EbMysteryUserCollect::getUid, uid)
                    .remove();
        }
        //添加记录前先重置自增值
        collectService.resetIncrement();
        //收藏
        return collectService.save(new EbMysteryUserCollect(uid, bid));
    }

    @Override
    public Boolean recyclePrize(MysteryRecyclePrizeDto recyclePrizeRequest) {
        //先看看开盒记录里面有没有该记录，并且没有被寄售、提货等
        EbMysteryOutOfRecord record = recordService.lambdaQuery()
                //记录编号
                .eq(EbMysteryOutOfRecord::getId, recyclePrizeRequest.getRid())
                //即使记录编号能够查到,但用户也必须是对应的用户才行
                .eq(EbMysteryOutOfRecord::getUid, uid)
                //记录类型为待提货,其他状态都不可回收
                .eq(EbMysteryOutOfRecord::getStatus, Constants.RECORD_WAIT_PICK)
                //查出记录编号和奖品的类型
                .select(EbMysteryOutOfRecord::getId, EbMysteryOutOfRecord::getEpid)
                .one();
        //如果记录不为空并奖品类型不为虚拟奖品,则代表可以回收
        if (record != null) {
            EbMysteryMerchandiseAssociated associated = associatedService.lambdaQuery().eq(EbMysteryMerchandiseAssociated::getId, record.getEpid())
                    //获得奖品类型 商品编号 回收折扣
                    .select(EbMysteryMerchandiseAssociated::getType, EbMysteryMerchandiseAssociated::getPid, EbMysteryMerchandiseAssociated::getRecyclingdiscount).one();
            //判断奖品类型
            if (!associated.getType()) {
                throw new MysteryException("抱歉,虚拟奖品不可回收!");
            }
            //记录是否有回收卡
            Integer haveRecCardId = null;
            //看看用户是否使用了回收卡,如果使用了则看看是否真的还有回收卡
            if (recyclePrizeRequest.getUseRecovery() != null && recyclePrizeRequest.getUseRecovery()) {
                //找到用户的卡卷
                EbMysteryUserCardVolume userRecCard = userCardVolumeService.lambdaQuery()
                        .eq(EbMysteryUserCardVolume::getUid, uid)
                        .eq(EbMysteryUserCardVolume::getType, Constants.CARD_RECYCLE)
                        .eq(EbMysteryUserCardVolume::getValid, Constants.CARD_VALID)
                        .select(EbMysteryUserCardVolume::getId)
                        .one();
                if (userRecCard == null) {
                    throw new MysteryException("回收卡无法使用!");
                }
                haveRecCardId = userRecCard.getId();
            }
            //回收费用公式 用户回收金额=商品原价*平台设置的回收折扣,如果有回收卡则按商品原价退款
            //获得商品原价
            EbStoreProduct product = ebStoreProductService.lambdaQuery()
                    .eq(EbStoreProduct::getId, associated.getPid())
                    .select(EbStoreProduct::getPrice, EbStoreProduct::getId)
                    .one();
            BigDecimal returnPrice = product.getPrice();
            if (haveRecCardId != null) {
                //用户回收卡-1
                userCardVolumeService.lambdaUpdate()
                        .eq(EbMysteryUserCardVolume::getId, haveRecCardId)
                        .setSql("valid = 2")
                        .update();
            } else {
                //获得平台设置的回收折扣退款
                Double recyclingDiscount = associated.getRecyclingdiscount();
                //计算退款金额
                returnPrice = returnPrice.multiply(BigDecimal.valueOf(recyclingDiscount));
            }
            //调用退款服务,向用户退款
            log.info(String.format("商家向用户%s退款了%sRMB", uid, returnPrice));
            //将状态改为->已回收
            recordService.lambdaUpdate()
                    .eq(EbMysteryOutOfRecord::getId, record.getId())
                    .setSql("status = 4")
                    .update();
            //添加回收记录
            EbMysteryRecycleRecord ebMysteryRecycleRecord = new EbMysteryRecycleRecord(uid, product.getId(), associated.getBid(), haveRecCardId != null, returnPrice);
            //添加前先重新计算一下自增值
            recycleRecordService.resetIncrement();
            recycleRecordService.save(ebMysteryRecycleRecord);
            return true;
        }
        throw new MysteryException("抱歉,该奖品无法被回收!");
    }

    @Override
    public Boolean creatMystery(NewMysteryDto newMysteryDto) {
        //将盲盒对象存放至数据库
        EbMystery ebMystery = new EbMystery();
        BeanUtils.copyProperties(newMysteryDto, ebMystery);
        ebMystery.setCreatby(newMysteryDto.getCreatby());
        //生成cron表达式
        String cronExpression = CronParser.createCronExpression(newMysteryDto.getCronScheduleModel());
        String description = CronParser.createDescription(newMysteryDto.getCronScheduleModel());
        ebMystery.setCycleCron(cronExpression);
        ebMystery.setCycleCronDes(description);
        ebMystery.setCreattime(new Date());
        //添加前 先重置一下自增值
        ebMysteryMapper.resetIncrement();
        //奖品集合
        List<EbMysteryMerchandiseAssociated> associatedList = newMysteryDto.getAssociatedList();
        ebMystery.setProductcount(associatedList.size());
        if (this.save(ebMystery)) {
            insertProduct(associatedList, ebMystery.getId());
            //如果盲盒保存成功，则根据盲盒的数据去开启对应的定时任务
            return createNewMysteryOpenJob(ebMystery);
        }
        return false;
    }


    /**
     * 给盲盒添加奖品
     *
     * @param associatedList 奖品集合
     * @param bid            盲盒编号
     */
    @Override
    public Boolean insertProduct(List<EbMysteryMerchandiseAssociated> associatedList, Integer bid) {
        //给盲盒添加奖品
        for (EbMysteryMerchandiseAssociated associated : associatedList) {
            associated.setBid(bid);
            associatedService.resetIncrement();
            //保存至数据库
            associatedService.save(associated);
        }
        //更新盲盒奖品数量
        LambdaQueryWrapper<EbMysteryMerchandiseAssociated> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(EbMysteryMerchandiseAssociated::getBid, bid);
        int count = associatedService.count(lambdaQueryWrapper);
        this.lambdaUpdate().eq(EbMystery::getId, bid)
                .setSql(String.format("productcount=%s", count)).update();
        return true;
    }

    private Boolean createNewMysteryOpenJob(EbMystery ebMystery) {
        createMysteryOpenJob(ebMystery);
        return true;
    }

    @Override
    public Boolean changeMysteryStatus(String bid) {
        int bidInt = Integer.parseInt(bid);
        //更新的字段  取反操作
        String setSql = "`status`=NOT `status`";
        //如果更新成功
        if (this.lambdaUpdate().eq(EbMystery::getId, bid).setSql(setSql).update()) {
            //并且重新查询一下该盲盒状态,如果是开启,就开启盲盒奖池的累计周期重置定时任务
            EbMystery nowMystery = this.lambdaQuery()
                    .eq(EbMystery::getId, bidInt)
                    .one();
            if (nowMystery.getStatus()) {
                log.info(nowMystery.getName() + "已开启");
                //如果管理员直接开启了盲盒,那么看有没有对应的定时器还在等待执行,如果有就删除掉
                scheduleJobService.deleteJob(nowMystery.getStartJob());
                //启用触发器被删除,取消关联
                this.lambdaUpdate().eq(EbMystery::getId, nowMystery.getId()).setSql("start_job= NULL ").update();
                //同时更新盲盒开启时间,删除活动开启的定时任务,避免手动开启时还是原来创建盲盒时的日期,所以统一就在开启时更新下实际开启日期
                EbMystery ebMystery1 = new EbMystery();
                ebMystery1.setId(nowMystery.getId());
                ebMystery1.setStarttime(new Date());
                this.updateById(ebMystery1);
                //创建盲盒奖池周期累计重置定时任务
                EbMysteryScheduleJob ebMysterySchedulePondResetJob = new EbMysteryScheduleJob();
                //按照开启的时间算
                ebMysterySchedulePondResetJob.setStartTime(new Date());
                ebMysterySchedulePondResetJob.setPauseTime(nowMystery.getEndtime());
                //设置周期的cron表达式
                ebMysterySchedulePondResetJob.setCron(nowMystery.getCycleCron());
                ebMysterySchedulePondResetJob.setBeanName(simpleName);
                ebMysterySchedulePondResetJob.setStatus(true);
                ebMysterySchedulePondResetJob.setParams(String.valueOf(nowMystery.getId()));
                ebMysterySchedulePondResetJob.setRemark("盲盒奖池周期累计重置定时任务:" + nowMystery.getCycleCronDes());
                ebMysterySchedulePondResetJob.setMethodName("resetMysteryPond");
                if (scheduleJobService.createJob(ebMysterySchedulePondResetJob)) {
                    this.lambdaUpdate().eq(EbMystery::getId, nowMystery.getId())
                            .setSql(String.format("pond_job=%s", ebMysterySchedulePondResetJob.getJobId())).update();
                    log.info("盲盒奖池周期累计重置定时任务创建成功！");
                }

                //只有盲盒开启成功,才能有结束定时任务
                //创建盲盒状态结束定时任务
                EbMysteryScheduleJob ebMysteryScheduleStatusEndJob = new EbMysteryScheduleJob();
                //开始时间与结束时间设置为同一个，就是只执行一次,状态关闭只执行一次
                ebMysteryScheduleStatusEndJob.setStartTime(nowMystery.getEndtime());
                ebMysteryScheduleStatusEndJob.setPauseTime(nowMystery.getEndtime());
                ebMysteryScheduleStatusEndJob.setBeanName(simpleName);
                ebMysteryScheduleStatusEndJob.setMethodName("changeMysteryStatus");
                ebMysteryScheduleStatusEndJob.setCron(tempCron);
                ebMysteryScheduleStatusEndJob.setStatus(true);
                ebMysteryScheduleStatusEndJob.setParams(String.valueOf(nowMystery.getId()));
                ebMysteryScheduleStatusEndJob.setRemark(nowMystery.getId() + nowMystery.getName() + "盲盒状态关闭");
                if (scheduleJobService.createJob(ebMysteryScheduleStatusEndJob)) {
                    this.lambdaUpdate().eq(EbMystery::getId, nowMystery.getId())
                            .setSql(String.format("pause_job=%s", ebMysteryScheduleStatusEndJob.getJobId())).update();
                    log.info("盲盒状态结束定时任务创建成功！");
                }
            } else {
                //盲盒关闭时,奖池累计和状态停用同时进行
                scheduleJobService.deleteJob(nowMystery.getPauseJob());
                scheduleJobService.deleteJob(nowMystery.getPondJob());
                log.info(nowMystery.getName() + "已关闭");
                this.lambdaUpdate().eq(EbMystery::getId, nowMystery.getId())
                        .setSql("start_job=NULL,pause_job=NULL,pond_job=NULL").update();
            }
            return true;
        }
        return false;
    }

    @Override
    public Boolean resetMysteryPond(String bid) {
        int bidInt = Integer.parseInt(bid);
        EbMystery ebMystery = this.lambdaQuery().eq(EbMystery::getId, bidInt).one();
        // 保存奖池重置累计记录
        EbMysteryPondRecord ebMysteryPondRecord = new EbMysteryPondRecord();
        ebMysteryPondRecord.setBeforeSum(ebMystery.getPond());
        ebMysteryPondRecord.setResettime(new Date());
        ebMysteryPondRecord.setBid(ebMystery.getId());
        pondRecordService.save(ebMysteryPondRecord);
        log.info("盲盒奖池累计重置中...");
        return this.lambdaUpdate().eq(EbMystery::getId, ebMystery.getId()).setSql("pond=0").update();
    }

    @Override
    public Boolean updateMystery(UpdateMysteryDto updateMysteryDto) {
        EbMystery ebMystery = new EbMystery();
        BeanUtils.copyProperties(updateMysteryDto, ebMystery);

        //如果是修改了开始时间,就需要更新开启定时任务的时间
        if (updateMysteryDto.getStarttime() != null) {
            log.info("管理员修改了开始时间");
            //获得当前开启定时器
            EbMystery tempMystery = this.lambdaQuery()
                    .eq(EbMystery::getId, ebMystery.getId())
                    .select(EbMystery::getStartJob, EbMystery::getEndtime, EbMystery::getName).one();
            if (tempMystery.getStartJob() != null) {
                //如果结束时间早于现在所修改的时间,则驳回修改请求
                if (updateMysteryDto.getStarttime().compareTo(tempMystery.getEndtime()) > 0) {
                    throw new MysteryException(tempMystery.getName() + "开启失败,原因:结束时间比开启时间早,请先修改结束时间!");
                }
                EbMysteryScheduleJob startJob = scheduleJobService.getById(tempMystery.getStartJob());
                //更改开启执行时间
                startJob.setStartTime(updateMysteryDto.getStarttime());
                startJob.setPauseTime(updateMysteryDto.getStarttime());
                scheduleJobService.updateJob(startJob);
            } else {
                //如果没有就创建
                createMysteryOpenJob(ebMystery);
            }
        }

        //如果是修改了结束时间,就需要更新结束定时器的时间
        if (updateMysteryDto.getEndtime() != null) {
            log.info("管理员修改了结束时间");
            //获得当前关闭定时器
            EbMystery tempMystery = this.lambdaQuery()
                    .eq(EbMystery::getId, ebMystery.getId())
                    .select(EbMystery::getPauseJob, EbMystery::getPondJob, EbMystery::getStatus).one();
            //同时更新奖池周期结束的时间,更新条件只能是盲盒开启时更新
            if (tempMystery.getStatus()) {
                EbMysteryScheduleJob pauseJob = scheduleJobService.getById(tempMystery.getPauseJob());
                //更改开启执行时间
                pauseJob.setStartTime(updateMysteryDto.getEndtime());
                pauseJob.setPauseTime(updateMysteryDto.getEndtime());
                scheduleJobService.updateJob(pauseJob);
                EbMysteryScheduleJob pondJob = scheduleJobService.getById(tempMystery.getPondJob());
                pondJob.setPauseTime(updateMysteryDto.getEndtime());
                scheduleJobService.updateJob(pondJob);
            }
        }

        //盲盒奖池周期被更改
        if (updateMysteryDto.getCronScheduleModel() != null) {
            log.info("管理员修改了奖池周期");
            //获得当前周期定时器
            EbMystery tempMystery = this.lambdaQuery()
                    .eq(EbMystery::getId, ebMystery.getId())
                    .select(EbMystery::getPondJob, EbMystery::getStatus).one();
            //生成cron表达式
            String cronExpression = CronParser.createCronExpression(updateMysteryDto.getCronScheduleModel());
            String description = CronParser.createDescription(updateMysteryDto.getCronScheduleModel());
            //这里修改的cron表达式只在盲盒开启时生效
            ebMystery.setCycleCron(cronExpression);
            //表达式说明
            ebMystery.setCycleCronDes(description);

            //以下是只有在盲盒开启时才能够修改的数据
            if (tempMystery.getStatus()) {
                EbMysteryScheduleJob pondJob = scheduleJobService.getById(tempMystery.getPondJob());
                //如果停用时间也被更改了,那么奖池周期的停止日期也需要随之改变
                if (updateMysteryDto.getEndtime() != null) {
                    pondJob.setPauseTime(updateMysteryDto.getEndtime());
                }
                pondJob.setCron(cronExpression);
                scheduleJobService.updateJob(pondJob);
            }
        }
        return this.updateById(ebMystery);
    }

    private void createMysteryOpenJob(EbMystery ebMystery) {
        //如果没有就创建一个
        EbMysteryScheduleJob startJob = new EbMysteryScheduleJob();
        //设置是否开启,默认直接开启
        startJob.setStatus(true);
        //设置开始时间
        startJob.setStartTime(ebMystery.getStarttime());
        //设置停用时间
        startJob.setPauseTime(ebMystery.getStarttime());
        //执行的IOC容器中的实例
        startJob.setBeanName(simpleName);
        //执行的方法
        startJob.setMethodName("changeMysteryStatus");
        //添加定时任务的说明
        startJob.setRemark("盲盒状态开启");
        //给定时任务方法传入参数，直接将当前创建的盲盒对象转换为json对象作为参数
        startJob.setParams(String.valueOf(ebMystery.getId()));
        //设置cron表达式
        startJob.setCron(tempCron);
        if (scheduleJobService.createJob(startJob)) {
            //更新盲盒中的定时任务绑定字段
            ebMystery.setStartJob(startJob.getJobId());
            this.updateById(ebMystery);
        }
    }

    @Override
    public PageInfo<EbStoreProductVo> getMerchantProduct(MerchantProductsSearchDto merchantProductsSearchDto, PageParamRequest pageParamRequest) {
        Page<EbStoreProductVo> ebMysteriesPage = PageHelper.startPage(pageParamRequest.getPage(), pageParamRequest.getLimit());
        LambdaQueryWrapper<EbStoreProduct> storeProductQueryWrapper = Wrappers.lambdaQuery();
        //判断类型
        //类型搜索
        switch (merchantProductsSearchDto.getType()) {
            case 1:
                //出售中（已上架）
                storeProductQueryWrapper.eq(EbStoreProduct::getIsShow, true);
                storeProductQueryWrapper.eq(EbStoreProduct::getIsDel, false);
                break;
            case 2:
                //仓库中（未上架）
                storeProductQueryWrapper.eq(EbStoreProduct::getIsShow, false);
                storeProductQueryWrapper.eq(EbStoreProduct::getIsDel, false);
                break;
            case 3:
                //已售罄
                storeProductQueryWrapper.le(EbStoreProduct::getStock, 0);
                storeProductQueryWrapper.eq(EbStoreProduct::getIsDel, false);
                break;
            case 4:
                //警戒库存
                Integer stock = 11;
                storeProductQueryWrapper.le(EbStoreProduct::getStock, stock);
                storeProductQueryWrapper.eq(EbStoreProduct::getIsDel, false);
                break;
            case 5:
                //回收站
                storeProductQueryWrapper.eq(EbStoreProduct::getIsDel, true);
                break;
            default:
                break;
        }

        //关键字搜索
        if (!StringUtils.isBlank(merchantProductsSearchDto.getKeywords())) {
            storeProductQueryWrapper.and(i -> i
                    .or().eq(EbStoreProduct::getId, merchantProductsSearchDto.getKeywords())
                    .or().like(EbStoreProduct::getStoreName, merchantProductsSearchDto.getKeywords())
                    .or().like(EbStoreProduct::getStoreInfo, merchantProductsSearchDto.getKeywords())
                    .or().like(EbStoreProduct::getKeyword, merchantProductsSearchDto.getKeywords())
                    .or().like(EbStoreProduct::getBarCode, merchantProductsSearchDto.getKeywords()));
        }

        //模拟当前商户id为0
        storeProductQueryWrapper.eq(EbStoreProduct::getMerId, merchantProductsSearchDto.getMerId());
        if (merchantProductsSearchDto.getIsShow() != null) {
            //是否上架
            storeProductQueryWrapper.eq(EbStoreProduct::getIsShow, merchantProductsSearchDto.getIsShow());
        }

        List<EbStoreProduct> list = ebStoreProductService.list(storeProductQueryWrapper);
        List<EbStoreProductVo> productVos = new ArrayList<>();
        //将查出来的数据绑定到响应的对象字段上
        for (EbStoreProduct product : list) {
            EbStoreProductVo ebStoreProductVo = new EbStoreProductVo();
            BeanUtils.copyProperties(product, ebStoreProductVo);
            productVos.add(ebStoreProductVo);
        }
        return CommonPage.copyPageInfo(ebMysteriesPage, productVos);
    }

    @Override
    public Boolean updatePrize(EbMysteryMerchandiseAssociated mysteryMerchandiseAssociated) {
        return associatedService.updateById(mysteryMerchandiseAssociated);
    }

    @Override
    public PageInfo<MysteryHistoryVo> getMysteryHistory(HistorySearchDto searchDto, PageParamRequest pageParamRequest) {
        Page<MysteryHistoryVo> historyVoPage = PageHelper.startPage(pageParamRequest.getPage(), pageParamRequest.getLimit());
        ArrayList<MysteryHistoryVo> mysteryHistoryVos = new ArrayList<>();
        LambdaQueryWrapper<EbMysteryOutOfRecord> recordLambdaQueryWrapper = Wrappers.lambdaQuery();
        if (searchDto.getStatus() != null) {
            recordLambdaQueryWrapper.eq(EbMysteryOutOfRecord::getStatus, searchDto.getStatus());
        }
        recordLambdaQueryWrapper.eq(EbMysteryOutOfRecord::getUid, uid);
        //获取当前用户的所有记录
        List<EbMysteryOutOfRecord> recordList = recordService.list(recordLambdaQueryWrapper);
        //遍历记录找到对应的盲盒
        for (EbMysteryOutOfRecord record : recordList) {
            //创建记录响应对象
            MysteryHistoryVo mysteryHistoryVo = new MysteryHistoryVo();
            mysteryHistoryVo.setRid(record.getId());
            mysteryHistoryVo.setStatus(record.getStatus());
            //找到对应的奖品扩展对象
            EbMysteryMerchandiseAssociated associated = associatedService.getById(record.getEpid());
            //通过记录获得盲盒对象
            EbMystery mystery = this.getById(associated.getBid());
            EbMysteryVo mysteryVo = new EbMysteryVo();
            //复制只需要的字段
            BeanUtils.copyProperties(mystery, mysteryVo);
            mysteryHistoryVo.setMysteryVo(mysteryVo);
            //创建奖品扩展响应对象
            EbMysteryMerchandiseAssociatedVo associatedVo = new EbMysteryMerchandiseAssociatedVo();
            BeanUtils.copyProperties(associated, associatedVo);
            //判断奖品的类型
            if (associatedVo.getType()) {
                //找到对应的商品
                EbStoreProduct product = ebStoreProductService.getById(associated.getPid());
                EbStoreProductVo ebStoreProductVo = new EbStoreProductVo();
                BeanUtils.copyProperties(product, ebStoreProductVo);
                associatedVo.setProduct(ebStoreProductVo);
            } else {
                //找到对应的卡卷
                EbMysteryCardVolume cardVolume = cardVolumeService.getById(associated.getPid());
                associatedVo.setCardVolume(cardVolume);
            }
            mysteryHistoryVo.setAssociatedVo(associatedVo);
            mysteryHistoryVos.add(mysteryHistoryVo);
        }
        return CommonPage.copyPageInfo(historyVoPage, mysteryHistoryVos);
    }

    /**
     * 原始商品转化为响应商品复用方法
     *
     * @param associated 原始商品对象
     * @return 返回转化后的响应商品对象
     */
    private EbMysteryMerchandiseAssociatedVo transMerchandiseAssociated(EbMysteryMerchandiseAssociated associated) {
        //创建响应对象,将抽到的商品信息填充进去
        EbMysteryMerchandiseAssociatedVo associatedResponse = new EbMysteryMerchandiseAssociatedVo();
        BeanUtils.copyProperties(associated, associatedResponse);
        //获得商品详细
        //判断商品的类型，如果是实物就去找商品服务，否则为虚拟奖品找卡卷服务
        if (associated.getType()) {
            EbStoreProduct product = ebStoreProductService.getById(associated.getPid());
            EbStoreProductVo ebStoreProductVo = new EbStoreProductVo();
            BeanUtils.copyProperties(product, ebStoreProductVo);
            //调用商品服务，得到商品的详细信息
            associatedResponse.setProduct(ebStoreProductVo);
        } else {
            //调用卡卷服务，获得卡卷的信息
            associatedResponse.setCardVolume(cardVolumeService.getById(associated.getPid()));
        }
        return associatedResponse;
    }

}